package cn.lutu.domain.service.impl;


import cn.lutu.cache.UserCacheRepository;
import cn.lutu.domain.repository.UserRepository;
import cn.lutu.domain.repository.UserRoleRepository;
import cn.lutu.protocol.dto.*;
import cn.lutu.dao.mapper.UserMapper;
import cn.lutu.domain.service.UserService;
import cn.lutu.protocol.entity.RoleUser;
import cn.lutu.protocol.entity.User;
import cn.lutu.protocol.entity.UserAndRole;
import cn.lutu.protocol.po.UserLoginInfoPO;
import cn.lutu.protocol.vo.UserLoginResultVO;
import cn.lutu.protocol.vo.UserVO;
import cn.lutu.security.security.CustomUserDetails;
import cn.lutu.util.exception.ServiceException;
import cn.lutu.util.response.StatusCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.management.relation.Role;
import java.util.*;


@Service
@Slf4j
public class UserServiceImpl implements UserService {


    @Value("${lutu.jwt.secret-key}")
    private String secretKey;
    @Value("${lutu.user.default-avatar}")
    private String avatar;

    @Value("${lutu.jwt.duration-in-minute}")
    private Long durationInMinute;

    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    UserMapper userMapper;
    @Autowired
    UserRepository userRepository;
    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    UserCacheRepository userCacheRepository;

    @Autowired
    UserRoleRepository userRoleRepository;

    public UserLoginResultVO login(UserLoginDTO userLoginDTO, String remoteAddr, String userAgent) {
        log.debug("开始处理【用户登录】的业务，参数：{}", userLoginDTO);
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                userLoginDTO.getUserName(), userLoginDTO.getPassword());
        log.debug("准备调用AuthenticationManager的认证方法，判断此用户名、密码是否可以成功登录……");
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.debug("验证用户登录成功，返回的认证结果：{}", authenticateResult);

        Object principal = authenticateResult.getPrincipal();
        log.debug("从认证结果中获取当事人：{}", principal);
        CustomUserDetails userDetails = (CustomUserDetails) principal;
        Long id = userDetails.getId();
        log.debug("从认证结果中的当事人中获取ID：{}", id);
        String nickName = userDetails.getNickName();
        log.debug("从认证结果中的当事人中获取昵称：{}", nickName);
        String userName = userDetails.getUsername();
        log.debug("从认证结果中的当事人中获取用户名：{}", userName);
        String imgUrl = userDetails.getImgUrl();
        log.debug("从认证结果中的当事人中获取头像：{}", imgUrl);
        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
        log.debug("从认证结果中的当事人中获取权限列表：{}", authorities);
        String authoritiesJsonString = JSON.toJSONString(authorities);
        log.debug("将权限列表对象转换为JSON格式的字符串：{}", authoritiesJsonString);

        Date date = new Date(System.currentTimeMillis() + 1L * 1000 * 60 * durationInMinute);
        //                                                  ↑ 注意加L，避免int溢出为负数
        //权限列表不再加入JWT,
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", id);
        claims.put("username", userName);
        String jwt = Jwts.builder()
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                .setClaims(claims)
                .setExpiration(date)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        //todo 生成jwt后将权限列表加入Redis中
        UserLoginInfoPO userLoginInfoPO = new UserLoginInfoPO();
        userLoginInfoPO.setIp(remoteAddr);
        userLoginInfoPO.setUserAgent(userAgent);
        userLoginInfoPO.setAuthoritiesJsonString(authoritiesJsonString);
        userCacheRepository.saveLoginInfo(jwt,userLoginInfoPO);

        /*将用户状态存入到Redis中*/
        userCacheRepository.saveEnableByUserId(id,1);
        UserLoginResultVO userLoginResultVO = new UserLoginResultVO()
                .setId(id)
                .setNickName(nickName)
                .setImgUrl(imgUrl)
                .setToken(jwt);
//         //改为使用JWT后，不必在登录成功后就将认证信息存入到SecurityContext中
//         log.debug("准备将认证信息结果存入到SecurityContext中……");
//         //SecurityContext securityContext = SecurityContextHolder.getContext();
//         //securityContext.setAuthentication(authenticateResult);
//         log.debug("已经将认证信息存入到SecurityContext中，登录业务处理完成！");
        return userLoginResultVO;
    }

    public void addNew(UserRegDTO userRegDTO){
        log.info("判断用户名是否存在"+userRegDTO);
        UserVO userVO = userRepository.selectByUserName(userRegDTO.getEmail());
        if(userVO != null){
            String message = "邮箱已注册！请重新提交";
            log.debug(message);
            throw new ServiceException(StatusCode.EMAIL_ALREADY_EXSTS,message);
        }

        User user =new User();
        BeanUtils.copyProperties(userRegDTO,user);
        log.info("复制后的USER为："+user);
        user.setImgUrl(avatar);
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setUserName(userRegDTO.getEmail());
        userRepository.insert(user);

        RoleUser roleUser =new RoleUser();
        log.debug("userID为{}",user.getId());
        roleUser.setUserId(user.getId());
        roleUser.setRoleId(3L);
        log.debug("roleId {}",roleUser.getRoleId());
        int i = userRoleRepository.insert(roleUser);
        if(i!=1){
            String message = "角色赋值失败";
            log.debug(message);
            throw new ServiceException(StatusCode.ERROR_UNKNOWN,message);
        }


    }

    public void updateInfoById(UserUpdateDTO userUpdateDTO,Long id){
        log.debug("开始执行【更新用户】的数据访问，参数：{}",  userUpdateDTO);
        log.debug("开始执行【更新用户】的数据访问，参数：{}",  id);
        UserVO userVO =userRepository.selectById(id);
        log.debug("userVO: {}",userVO);
        if (userVO == null) {
            String message ="更新失败，尝试更新的数据不存在！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_NOT_FOUND, message);
        }
        User user = new User();
        BeanUtils.copyProperties(userUpdateDTO,user);
        user.setImgUrl(userUpdateDTO.getImgUrl());

        if (user.getNickName()!="" && user.getImgUrl()!="") {
            userRepository.updateInfoById(user);
            log.info("输出user,{}",user);
        }else {
            String message = "更新失败，数据不能为空！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_NOT_FOUND, message);
        }

    }

    @Override
    public UserVO selectById(Long id) {
        log.debug("开始处理【根据ID查询用户】业务，参数：{}", id);
        UserVO currentUser = userRepository.selectById(id);
        if (currentUser == null) {
            String message = "获取用户详情失败，尝试访问的用户数据不存在！";
            log.warn(message);
            throw new ServiceException(StatusCode.ERROR_NOT_FOUND, message);
        }
        return currentUser;
    }




    @Override
    public User retrievePasswordUser(RetrievePasswordDTO userChangePasswordDTO) {
        User user= new User();
        BeanUtils.copyProperties(userChangePasswordDTO,user);
        UserVO userVO=userMapper.selectByEmail(userChangePasswordDTO.getEmail());
        user.setId(userVO.getId());
        user.setPassword(passwordEncoder.encode(userChangePasswordDTO.getPassword()));
       // userMapper.update(user);
        return user;
    }


    public void changePasswordUser(UserChangePasswordDTO userChangePasswordDTO,Long id){
        log.debug("开始执行【更新用户】的密码访问，参数：{}",  userChangePasswordDTO);
        log.debug("开始执行【更新用户】的密码访问，参数：{}",  id);
        log.debug("密码是否相等, 参数: {}",passwordEncoder.encode(userChangePasswordDTO.getOldPassword()));
        UserVO userVO =userRepository.selectById(id);
        log.debug("原密码, 参数:{}",userVO.getPassword());
        User user =new User();

        if(new BCryptPasswordEncoder().matches(userChangePasswordDTO.getOldPassword(), userVO.getPassword())) {
            if (userChangePasswordDTO.getCheckPassword().equals(userChangePasswordDTO.getNewPassword())) {
                user.setPassword(passwordEncoder.encode(userChangePasswordDTO.getNewPassword()));
                user.setId(id);
                userRepository.updatePassword(user);
            }else {
                String message = "修改密码失败，再次输入的密码与新密码不一致!";
                log.warn(message);
                throw new ServiceException(StatusCode.PASSWORD_ERROR, message);
            }
        }else {
            String message = "修改密码失败，旧密码输入错误!";
            log.warn(message);
            throw new ServiceException(StatusCode.PASSWORD_ERROR, message);
        }

    }
}

